﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace ProtocolBuilder
{
    class Field
    {
        private XElement xmlElement;

        public XElement XElement
        {
            get
            {
                return this.xmlElement;
            }
        }

        public string FieldName
        {
            get
            {
                return this.xmlElement.Attribute("name").Value;
            }
            set
            {
                this.xmlElement.Attribute("name").Value = value;
            }
        }

        public string Description
        {
            get
            {
                return this.xmlElement.Attribute("description").Value;
            }
            set
            {
                this.xmlElement.Attribute("description").Value = value;
            }
        }

        public string FieldType
        {
            get
            {
                return this.xmlElement.Attribute("type").Value;
            }
            set
            {
                this.xmlElement.Attribute("type").Value = value;
            }
        }

        public FieldTemplete Templete
        {
            get
            {
                return TempleteLoader.GetTemplete(this.FieldType);
            }
        }

        public bool CanHasChildren
        {
            get
            {
                return this.Templete.CanHasChildren;
            }
        }

        public IEnumerable<Field> GetChildrenField()
        {
            if (!this.CanHasChildren)
            {
                throw new InvalidOperationException("this field can not has children.");
            }

            IList<Field> result = new List<Field>();
            foreach (XElement child in this.xmlElement.Elements("field"))
            {
                result.Add(FromXml(child));
            }

            return result;
        }

        public Field(bool initialize)
        {
            if (initialize)
            {
                this.Initialize();
            }
        }

        public void RemoveAllFields()
        {
            this.xmlElement.RemoveNodes();
        }

        public void AddField(Field field)
        {
            this.xmlElement.Add(field.XElement);
        }

        public static Field FromXml(XElement element)
        {
            if (element == null || element.Name.Equals("field"))
            {
                throw new ArgumentException("not a field element.");
            }

            Field result = new Field(false);
            result.xmlElement = element;

            return result;
        }

        public string GenerateWriteCode()
        {
            string prefix = this.GenerateWhiteSpace();
            int generation = this.GetGeneration();
            string result = prefix;
            if (!this.CanHasChildren)
            {
                result += this.Templete.WriteCode;
                result += "//";
                result += this.Description;
            }
            else
            {
                result += string.Format("for(int i{0} = 0;i{0} < @p1;i{0}++)", generation == 0 ? string.Empty : generation.ToString());
                result += TextHelper.HtmlNewLine;
                result += string.Format("{0}{{", prefix);
                result += TextHelper.HtmlNewLine;
                foreach (Field child in this.GetChildrenField())
                {
                    result += child.GenerateWriteCode();
                    result += TextHelper.HtmlNewLine;
                }
                result += string.Format("{0}}}", prefix);
            }

            return result;
        }

        public string GenerateReadCode()
        {
            string prefix = this.GenerateWhiteSpace();
            int generation = this.GetGeneration();
            string result = prefix;
            if (!this.CanHasChildren)
            {
                result += this.Templete.ReadCode;
                result += "//";
                result += this.Description;
            }
            else
            {
                result += string.Format("for(int i{0} = 0;i{0} < @p1;i{0}++)", generation == 0 ? string.Empty : generation.ToString());
                result += TextHelper.HtmlNewLine;
                result += string.Format("{0}{{", prefix);
                result += TextHelper.HtmlNewLine;
                foreach (Field child in this.GetChildrenField())
                {
                    result += child.GenerateReadCode();
                    result += TextHelper.HtmlNewLine;
                }
                result += string.Format("{0}}}", prefix);
            }

            return result;
        }

        private void Initialize()
        {
            this.xmlElement = new XElement("field", new XAttribute("name", string.Empty),
                new XAttribute("description", string.Empty),
                new XAttribute("type", string.Empty));
        }

        private string GenerateWhiteSpace()
        {
            string prefix = string.Empty;
            int generation = this.GetGeneration();
            for (int i = 0; i < generation; i++)
            {
                prefix += TextHelper.HtmlTab;
            }

            return prefix;
        }

        private int GetGeneration()
        {
            int result = -1;    //except the parent is the packet node.
            XElement parent = this.xmlElement.Parent;
            while (parent != null)
            {
                result++;
                parent = parent.Parent;
            }

            return result;
        }
    }
}